Skip to content

[DXC] Add -metal flag to DXC driver #130173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 7, 2025
Merged

Conversation

llvm-beanz
Copy link
Collaborator

This adds a flag to the DXC driver to enable calling the metal shader converter if it is available to convert the final shader output for metal.

This adds a flag to the DXC driver to enable calling the metal shader
converter if it is available to convert the final shader output for
metal.
@llvm-beanz llvm-beanz requested a review from bogner March 6, 2025 20:34
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' HLSL HLSL Language Support labels Mar 6, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 6, 2025

@llvm/pr-subscribers-clang-driver

@llvm/pr-subscribers-hlsl

Author: Chris B (llvm-beanz)

Changes

This adds a flag to the DXC driver to enable calling the metal shader converter if it is available to convert the final shader output for metal.


Full diff: https://github.com/llvm/llvm-project/pull/130173.diff

8 Files Affected:

  • (modified) clang/include/clang/Driver/Action.h (+13-1)
  • (modified) clang/include/clang/Driver/Options.td (+1)
  • (modified) clang/lib/Driver/Action.cpp (+8)
  • (modified) clang/lib/Driver/Driver.cpp (+10)
  • (modified) clang/lib/Driver/ToolChain.cpp (+1)
  • (modified) clang/lib/Driver/ToolChains/HLSL.cpp (+20)
  • (modified) clang/lib/Driver/ToolChains/HLSL.h (+14)
  • (added) clang/test/Driver/HLSL/metal-converter.hlsl (+14)
diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h
index e5307b0fcedd5..92bb19314e3d6 100644
--- a/clang/include/clang/Driver/Action.h
+++ b/clang/include/clang/Driver/Action.h
@@ -75,9 +75,10 @@ class Action {
     LinkerWrapperJobClass,
     StaticLibJobClass,
     BinaryAnalyzeJobClass,
+    BinaryTranslatorJobClass,
 
     JobClassFirst = PreprocessJobClass,
-    JobClassLast = BinaryAnalyzeJobClass
+    JobClassLast = BinaryTranslatorJobClass
   };
 
   // The offloading kind determines if this action is binded to a particular
@@ -675,6 +676,17 @@ class BinaryAnalyzeJobAction : public JobAction {
   }
 };
 
+class BinaryTranslatorJobAction : public JobAction {
+  void anchor() override;
+
+public:
+  BinaryTranslatorJobAction(Action *Input, types::ID Type);
+
+  static bool classof(const Action *A) {
+    return A->getKind() == BinaryTranslatorJobClass;
+  }
+};
+
 } // namespace driver
 } // namespace clang
 
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index d0414aba35209..6ed579cb88d90 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9085,6 +9085,7 @@ def : Option<["/", "-"], "Qembed_debug", KIND_FLAG>, Group<dxc_Group>,
   HelpText<"Embed PDB in shader container (ignored)">;
 def spirv : DXCFlag<"spirv">,
   HelpText<"Generate SPIR-V code">;
+def metal : DXCFlag<"metal">, HelpText<"Generate Metal library">;
 def fspv_target_env_EQ : Joined<["-"], "fspv-target-env=">, Group<dxc_Group>,
   HelpText<"Specify the target environment">,
   Values<"vulkan1.2, vulkan1.3">;
diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp
index 0899b8ef00152..ec09726044812 100644
--- a/clang/lib/Driver/Action.cpp
+++ b/clang/lib/Driver/Action.cpp
@@ -50,6 +50,8 @@ const char *Action::getClassName(ActionClass AC) {
     return "static-lib-linker";
   case BinaryAnalyzeJobClass:
     return "binary-analyzer";
+  case BinaryTranslatorJobClass:
+    return "binary-translator";
   }
 
   llvm_unreachable("invalid class");
@@ -459,3 +461,9 @@ void BinaryAnalyzeJobAction::anchor() {}
 
 BinaryAnalyzeJobAction::BinaryAnalyzeJobAction(Action *Input, types::ID Type)
     : JobAction(BinaryAnalyzeJobClass, Input, Type) {}
+
+void BinaryTranslatorJobAction::anchor() {}
+
+BinaryTranslatorJobAction::BinaryTranslatorJobAction(Action *Input,
+                                                     types::ID Type)
+    : JobAction(BinaryTranslatorJobClass, Input, Type) {}
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index eca96c1cce7f7..565d804aa8187 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4672,6 +4672,16 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
       Actions.push_back(C.MakeAction<BinaryAnalyzeJobAction>(
           LastAction, types::TY_DX_CONTAINER));
     }
+    if (Args.getLastArg(options::OPT_metal)) {
+      Action *LastAction = Actions.back();
+      // Metal shader converter runs on DXIL containers, which can either be
+      // validated (in which case they are TY_DX_CONTAINER), or unvalidated
+      // (TY_OBJECT).
+      if (LastAction->getType() == types::TY_DX_CONTAINER ||
+          LastAction->getType() == types::TY_Object)
+        Actions.push_back(C.MakeAction<BinaryTranslatorJobAction>(
+            LastAction, types::TY_DX_CONTAINER));
+    }
   }
 
   // Claim ignored clang-cl options.
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 65acbe8a9dbea..fa810457a06cd 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -639,6 +639,7 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const {
   case Action::DsymutilJobClass:
   case Action::VerifyDebugInfoJobClass:
   case Action::BinaryAnalyzeJobClass:
+  case Action::BinaryTranslatorJobClass:
     llvm_unreachable("Invalid tool kind.");
 
   case Action::CompileJobClass:
diff --git a/clang/lib/Driver/ToolChains/HLSL.cpp b/clang/lib/Driver/ToolChains/HLSL.cpp
index ad44c2cfcd811..62e4d14390b90 100644
--- a/clang/lib/Driver/ToolChains/HLSL.cpp
+++ b/clang/lib/Driver/ToolChains/HLSL.cpp
@@ -198,6 +198,22 @@ void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,
                                          Exec, CmdArgs, Inputs, Input));
 }
 
+void tools::hlsl::MetalConverter::ConstructJob(
+    Compilation &C, const JobAction &JA, const InputInfo &Output,
+    const InputInfoList &Inputs, const ArgList &Args,
+    const char *LinkingOutput) const {
+  std::string MSCPath = getToolChain().GetProgramPath("metal-shaderconverter");
+  ArgStringList CmdArgs;
+  const InputInfo &Input = Inputs[0];
+  CmdArgs.push_back(Input.getFilename());
+  CmdArgs.push_back("-o");
+  CmdArgs.push_back(Input.getFilename());
+
+  const char *Exec = Args.MakeArgString(MSCPath);
+  C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
+                                         Exec, CmdArgs, Inputs, Input));
+}
+
 /// DirectX Toolchain
 HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
                              const ArgList &Args)
@@ -214,6 +230,10 @@ Tool *clang::driver::toolchains::HLSLToolChain::getTool(
     if (!Validator)
       Validator.reset(new tools::hlsl::Validator(*this));
     return Validator.get();
+  case Action::BinaryTranslatorJobClass:
+    if (!MetalConverter)
+      MetalConverter.reset(new tools::hlsl::MetalConverter(*this));
+    return MetalConverter.get();
   default:
     return ToolChain::getTool(AC);
   }
diff --git a/clang/lib/Driver/ToolChains/HLSL.h b/clang/lib/Driver/ToolChains/HLSL.h
index b2a31aabab7dc..86dd65f0b80c6 100644
--- a/clang/lib/Driver/ToolChains/HLSL.h
+++ b/clang/lib/Driver/ToolChains/HLSL.h
@@ -29,6 +29,19 @@ class LLVM_LIBRARY_VISIBILITY Validator : public Tool {
                     const llvm::opt::ArgList &TCArgs,
                     const char *LinkingOutput) const override;
 };
+
+class LLVM_LIBRARY_VISIBILITY MetalConverter : public Tool {
+public:
+  MetalConverter(const ToolChain &TC)
+      : Tool("hlsl::MetalConverter", "metal-shaderconverter", TC) {}
+
+  bool hasIntegratedCPP() const override { return false; }
+
+  void ConstructJob(Compilation &C, const JobAction &JA,
+                    const InputInfo &Output, const InputInfoList &Inputs,
+                    const llvm::opt::ArgList &TCArgs,
+                    const char *LinkingOutput) const override;
+};
 } // namespace hlsl
 } // namespace tools
 
@@ -57,6 +70,7 @@ class LLVM_LIBRARY_VISIBILITY HLSLToolChain : public ToolChain {
 
 private:
   mutable std::unique_ptr<tools::hlsl::Validator> Validator;
+  mutable std::unique_ptr<tools::hlsl::MetalConverter> MetalConverter;
 };
 
 } // end namespace toolchains
diff --git a/clang/test/Driver/HLSL/metal-converter.hlsl b/clang/test/Driver/HLSL/metal-converter.hlsl
new file mode 100644
index 0000000000000..4402e5044dc7b
--- /dev/null
+++ b/clang/test/Driver/HLSL/metal-converter.hlsl
@@ -0,0 +1,14 @@
+// RUN: %clang_dxc -T cs_6_0 %s -metal -Fo tmp.mtl -### 2>&1 | FileCheck %s
+// RUN: %clang_dxc -T cs_6_0 %s -metal -Vd -Fo tmp.mtl -### 2>&1 | FileCheck %s
+// CHECK: "{{.*}}metal-shaderconverter{{(.exe)?}}" "tmp.mtl" "-o" "tmp.mtl"
+
+// RUN: %clang_dxc -T cs_6_0 %s -metal -### 2>&1 | FileCheck --check-prefix=NO_MTL %s
+// NO_MTL-NOT: metal-shaderconverter
+
+RWBuffer<float4> In : register(u0, space0);
+RWBuffer<float4> Out : register(u1, space4);
+
+[numthreads(1,1,1)]
+void main(uint GI : SV_GroupIndex) {
+  Out[GI] = In[GI] * In[GI];
+}

@llvmbot
Copy link
Member

llvmbot commented Mar 6, 2025

@llvm/pr-subscribers-clang

Author: Chris B (llvm-beanz)

Changes

This adds a flag to the DXC driver to enable calling the metal shader converter if it is available to convert the final shader output for metal.


Full diff: https://github.com/llvm/llvm-project/pull/130173.diff

8 Files Affected:

  • (modified) clang/include/clang/Driver/Action.h (+13-1)
  • (modified) clang/include/clang/Driver/Options.td (+1)
  • (modified) clang/lib/Driver/Action.cpp (+8)
  • (modified) clang/lib/Driver/Driver.cpp (+10)
  • (modified) clang/lib/Driver/ToolChain.cpp (+1)
  • (modified) clang/lib/Driver/ToolChains/HLSL.cpp (+20)
  • (modified) clang/lib/Driver/ToolChains/HLSL.h (+14)
  • (added) clang/test/Driver/HLSL/metal-converter.hlsl (+14)
diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h
index e5307b0fcedd5..92bb19314e3d6 100644
--- a/clang/include/clang/Driver/Action.h
+++ b/clang/include/clang/Driver/Action.h
@@ -75,9 +75,10 @@ class Action {
     LinkerWrapperJobClass,
     StaticLibJobClass,
     BinaryAnalyzeJobClass,
+    BinaryTranslatorJobClass,
 
     JobClassFirst = PreprocessJobClass,
-    JobClassLast = BinaryAnalyzeJobClass
+    JobClassLast = BinaryTranslatorJobClass
   };
 
   // The offloading kind determines if this action is binded to a particular
@@ -675,6 +676,17 @@ class BinaryAnalyzeJobAction : public JobAction {
   }
 };
 
+class BinaryTranslatorJobAction : public JobAction {
+  void anchor() override;
+
+public:
+  BinaryTranslatorJobAction(Action *Input, types::ID Type);
+
+  static bool classof(const Action *A) {
+    return A->getKind() == BinaryTranslatorJobClass;
+  }
+};
+
 } // namespace driver
 } // namespace clang
 
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index d0414aba35209..6ed579cb88d90 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9085,6 +9085,7 @@ def : Option<["/", "-"], "Qembed_debug", KIND_FLAG>, Group<dxc_Group>,
   HelpText<"Embed PDB in shader container (ignored)">;
 def spirv : DXCFlag<"spirv">,
   HelpText<"Generate SPIR-V code">;
+def metal : DXCFlag<"metal">, HelpText<"Generate Metal library">;
 def fspv_target_env_EQ : Joined<["-"], "fspv-target-env=">, Group<dxc_Group>,
   HelpText<"Specify the target environment">,
   Values<"vulkan1.2, vulkan1.3">;
diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp
index 0899b8ef00152..ec09726044812 100644
--- a/clang/lib/Driver/Action.cpp
+++ b/clang/lib/Driver/Action.cpp
@@ -50,6 +50,8 @@ const char *Action::getClassName(ActionClass AC) {
     return "static-lib-linker";
   case BinaryAnalyzeJobClass:
     return "binary-analyzer";
+  case BinaryTranslatorJobClass:
+    return "binary-translator";
   }
 
   llvm_unreachable("invalid class");
@@ -459,3 +461,9 @@ void BinaryAnalyzeJobAction::anchor() {}
 
 BinaryAnalyzeJobAction::BinaryAnalyzeJobAction(Action *Input, types::ID Type)
     : JobAction(BinaryAnalyzeJobClass, Input, Type) {}
+
+void BinaryTranslatorJobAction::anchor() {}
+
+BinaryTranslatorJobAction::BinaryTranslatorJobAction(Action *Input,
+                                                     types::ID Type)
+    : JobAction(BinaryTranslatorJobClass, Input, Type) {}
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index eca96c1cce7f7..565d804aa8187 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4672,6 +4672,16 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
       Actions.push_back(C.MakeAction<BinaryAnalyzeJobAction>(
           LastAction, types::TY_DX_CONTAINER));
     }
+    if (Args.getLastArg(options::OPT_metal)) {
+      Action *LastAction = Actions.back();
+      // Metal shader converter runs on DXIL containers, which can either be
+      // validated (in which case they are TY_DX_CONTAINER), or unvalidated
+      // (TY_OBJECT).
+      if (LastAction->getType() == types::TY_DX_CONTAINER ||
+          LastAction->getType() == types::TY_Object)
+        Actions.push_back(C.MakeAction<BinaryTranslatorJobAction>(
+            LastAction, types::TY_DX_CONTAINER));
+    }
   }
 
   // Claim ignored clang-cl options.
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 65acbe8a9dbea..fa810457a06cd 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -639,6 +639,7 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const {
   case Action::DsymutilJobClass:
   case Action::VerifyDebugInfoJobClass:
   case Action::BinaryAnalyzeJobClass:
+  case Action::BinaryTranslatorJobClass:
     llvm_unreachable("Invalid tool kind.");
 
   case Action::CompileJobClass:
diff --git a/clang/lib/Driver/ToolChains/HLSL.cpp b/clang/lib/Driver/ToolChains/HLSL.cpp
index ad44c2cfcd811..62e4d14390b90 100644
--- a/clang/lib/Driver/ToolChains/HLSL.cpp
+++ b/clang/lib/Driver/ToolChains/HLSL.cpp
@@ -198,6 +198,22 @@ void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,
                                          Exec, CmdArgs, Inputs, Input));
 }
 
+void tools::hlsl::MetalConverter::ConstructJob(
+    Compilation &C, const JobAction &JA, const InputInfo &Output,
+    const InputInfoList &Inputs, const ArgList &Args,
+    const char *LinkingOutput) const {
+  std::string MSCPath = getToolChain().GetProgramPath("metal-shaderconverter");
+  ArgStringList CmdArgs;
+  const InputInfo &Input = Inputs[0];
+  CmdArgs.push_back(Input.getFilename());
+  CmdArgs.push_back("-o");
+  CmdArgs.push_back(Input.getFilename());
+
+  const char *Exec = Args.MakeArgString(MSCPath);
+  C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
+                                         Exec, CmdArgs, Inputs, Input));
+}
+
 /// DirectX Toolchain
 HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
                              const ArgList &Args)
@@ -214,6 +230,10 @@ Tool *clang::driver::toolchains::HLSLToolChain::getTool(
     if (!Validator)
       Validator.reset(new tools::hlsl::Validator(*this));
     return Validator.get();
+  case Action::BinaryTranslatorJobClass:
+    if (!MetalConverter)
+      MetalConverter.reset(new tools::hlsl::MetalConverter(*this));
+    return MetalConverter.get();
   default:
     return ToolChain::getTool(AC);
   }
diff --git a/clang/lib/Driver/ToolChains/HLSL.h b/clang/lib/Driver/ToolChains/HLSL.h
index b2a31aabab7dc..86dd65f0b80c6 100644
--- a/clang/lib/Driver/ToolChains/HLSL.h
+++ b/clang/lib/Driver/ToolChains/HLSL.h
@@ -29,6 +29,19 @@ class LLVM_LIBRARY_VISIBILITY Validator : public Tool {
                     const llvm::opt::ArgList &TCArgs,
                     const char *LinkingOutput) const override;
 };
+
+class LLVM_LIBRARY_VISIBILITY MetalConverter : public Tool {
+public:
+  MetalConverter(const ToolChain &TC)
+      : Tool("hlsl::MetalConverter", "metal-shaderconverter", TC) {}
+
+  bool hasIntegratedCPP() const override { return false; }
+
+  void ConstructJob(Compilation &C, const JobAction &JA,
+                    const InputInfo &Output, const InputInfoList &Inputs,
+                    const llvm::opt::ArgList &TCArgs,
+                    const char *LinkingOutput) const override;
+};
 } // namespace hlsl
 } // namespace tools
 
@@ -57,6 +70,7 @@ class LLVM_LIBRARY_VISIBILITY HLSLToolChain : public ToolChain {
 
 private:
   mutable std::unique_ptr<tools::hlsl::Validator> Validator;
+  mutable std::unique_ptr<tools::hlsl::MetalConverter> MetalConverter;
 };
 
 } // end namespace toolchains
diff --git a/clang/test/Driver/HLSL/metal-converter.hlsl b/clang/test/Driver/HLSL/metal-converter.hlsl
new file mode 100644
index 0000000000000..4402e5044dc7b
--- /dev/null
+++ b/clang/test/Driver/HLSL/metal-converter.hlsl
@@ -0,0 +1,14 @@
+// RUN: %clang_dxc -T cs_6_0 %s -metal -Fo tmp.mtl -### 2>&1 | FileCheck %s
+// RUN: %clang_dxc -T cs_6_0 %s -metal -Vd -Fo tmp.mtl -### 2>&1 | FileCheck %s
+// CHECK: "{{.*}}metal-shaderconverter{{(.exe)?}}" "tmp.mtl" "-o" "tmp.mtl"
+
+// RUN: %clang_dxc -T cs_6_0 %s -metal -### 2>&1 | FileCheck --check-prefix=NO_MTL %s
+// NO_MTL-NOT: metal-shaderconverter
+
+RWBuffer<float4> In : register(u0, space0);
+RWBuffer<float4> Out : register(u1, space4);
+
+[numthreads(1,1,1)]
+void main(uint GI : SV_GroupIndex) {
+  Out[GI] = In[GI] * In[GI];
+}

@llvm-beanz llvm-beanz merged commit 0ea5223 into llvm:main Mar 7, 2025
16 checks passed
@damyanp damyanp moved this to Closed in HLSL Support Apr 25, 2025
@damyanp damyanp removed this from HLSL Support Jun 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category HLSL HLSL Language Support
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants